package applet;

import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.Queue;

/** A class which keeps track of the mouse position and button states. */
public class Mouse extends MouseAdapter {
	/** */
	public static enum Button {
		/** */
		NONE(MouseEvent.NOBUTTON), LEFT(MouseEvent.BUTTON1),
		/** */
		MIDDLE(MouseEvent.BUTTON2), RIGHT(MouseEvent.BUTTON3);

		/** */
		private final int code;

		/**
		 * @param newCode
		 *            - the code.
		 */
		private Button(final int newCode) {
			code = newCode;
		}

		/** @return the code of this button. */
		public int getCode() {
			return code;
		}

		/**
		 * @return the button with the given code or null.
		 * @param code
		 *            - the code.
		 */
		public static Button getFromCode(final int code) {
			for (Button b : values()) {
				if (b.code == code) {
					return b;
				}
			}
			return null;
		}
	}

	/** */
	public static enum EventType {
		/** */
		NONE, PRESS, RELEASE, MOVE, DRAG;
	}

	/** */
	public static class Event {
		/** */
		private EventType type;
		/** */
		private Button button;
		/** */
		private Point position;

		/**
		 * @param newType
		 *            - the type of this event.
		 * @param newButton
		 *            - the button associated with this event.
		 * @param newPosition
		 *            - the position of this event.
		 */
		public Event(final EventType newType, final Button newButton,
				final Point newPosition) {
			type = newType;
			button = newButton;
			position = newPosition;
		}

		/** @return the type of this event. */
		public final EventType getType() {
			return type;
		}

		/** @return the button associated with this event. */
		public final Button getButton() {
			return button;
		}

		/** @return the position of this event. */
		public final Point getPosition() {
			return position;
		}
	}

	/** */
	private Queue<Event> queue;

	/** */
	private boolean[] buttonDown;
	/** */
	private boolean onScreen;
	/** */
	private Point position;

	/** */
	public Mouse() {
		buttonDown = new boolean[MouseInfo.getNumberOfButtons()];
		onScreen = true;
		position = new Point(0, 0);
		queue = new LinkedList<Event>();
	}

	/** @return the next event in the queue or null if the queue is empty. */
	public final synchronized Event pollEvent() {
		return queue.poll();
	}

	/**
	 * @param event
	 *            - the event to add to the queue.
	 */
	public final synchronized void addEvent(final Event event) {
		queue.add(event);
	}

	/**
	 * @param button
	 *            - the button.
	 * @return true if the given button is pressed.
	 */
	public final boolean getButton(final int button) {
		return buttonDown[button];
	}

	/** @return whether the mouse is over the game or not. */
	public final boolean getOnScreen() {
		return onScreen;
	}

	/** @return the current position of the cursor. */
	public final Point getPosition() {
		return new Point(position);
	}

	/** @return the current x position of the cursor. */
	public final int getX() {
		return position.x;
	}

	/** @return the current y position of the cursor. */
	public final int getY() {
		return position.y;
	}

	@Override
	public final synchronized void mousePressed(final MouseEvent e) {
		buttonDown[e.getButton() - 1] = true;
		queue.add(new Event(EventType.PRESS, Button.getFromCode(e.getButton()),
				e.getPoint()));
	}

	@Override
	public final synchronized void mouseReleased(final MouseEvent e) {
		buttonDown[e.getButton() - 1] = false;
		queue.add(new Event(EventType.RELEASE, Button
				.getFromCode(e.getButton()), e.getPoint()));
	}

	@Override
	public final synchronized void mouseEntered(final MouseEvent e) {
		onScreen = true;
	}

	@Override
	public final synchronized void mouseExited(final MouseEvent e) {
		onScreen = false;
	}

	@Override
	public final synchronized void mouseDragged(final MouseEvent e) {
		position = e.getPoint();
		queue.add(new Event(EventType.DRAG, Button.getFromCode(e.getButton()),
				e.getPoint()));
	}

	@Override
	public final synchronized void mouseMoved(final MouseEvent e) {
		position = e.getPoint();
		queue.add(new Event(EventType.MOVE, Button.getFromCode(e.getButton()),
				e.getPoint()));
	}
}
